# -*- coding: UTF-8 -*-
"""
@Project ：black-white-site-remote
@File    ：image_cache.py
@IDE     ：PyCharm
@Author  ：Tang
@Date    ：2021/10/17 0:59
"""
import os
import random
import re
import time
from io import BytesIO

from PIL import Image, ImageDraw, ImageFont
from random import choice, randint, sample, shuffle

from BlackWhiteSite.settings import BASE_DIR


class DrawCache:
    """生成图形验证码"""

    def __init__(self, w, h):
        if not w or not h or (w % 100 or h % 100):
            raise ValueError("Draw width or height err")
        self.__normal = "ABCDEFGHJKLMNOPQRSTUVWXYZABCDEFGHJKLMNOPQRSTUVWXYZ" * int((w * h // 10000))
        self.__special = "abcdefghijklmnopqrstuvwxyz" * int((w * h // 10000))
        if int(time.time()) % 2:
            self.__normal, self.__special = self.__special, self.__normal
        self.__byte_io = BytesIO()
        self.__width = w
        self.__height = h
        self.__cell_size = 20
        self.__num = int((self.__width * self.__height) / (self.__cell_size ** 2))
        self.__background = (250, 250, 250)
        self.__offset_x = 5
        self.__offset_y = 0
        self.__path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        self.__font = ImageFont.truetype(os.path.join(self.__path, 'static', 'fonts', 'simhei.ttf'), 20)  # 加载字体 是Windows自带的

    def get_one(self):
        """生成图片验证码"""
        byte_io = BytesIO()
        # 创建新图像
        image = Image.new("RGB", (self.__width, self.__height), self.__background)
        draw = ImageDraw.Draw(image)  # 创建画布
        content, idx_, special = self.__create_cache_content()
        x, y = self.__get_true_position(idx_)  # 获取目标值位置
        for idx, i in enumerate(content):  # 随机验证码
            # 为每个验证码字符设置不同的RGB颜色
            r, g, b = self.__set_text_color()
            draw.text((self.__offset_x, self.__offset_y), text=i, font=self.__font, fill=f"rgb({r},{g},{b})", direction=None)
            if not (idx + 1) % int(self.__width // 20):
                self.__offset_y += 20
                self.__offset_x = 5
            else:
                self.__offset_x = 5 if self.__offset_x == self.__width else self.__offset_x + 20
        image = self.__set_noise(image)
        image.save(byte_io, 'png')
        tag = "大" if special.isupper() else "小"
        return byte_io.getvalue(), (x+10, y+10), f"请点击下图中{tag}写的 '{special}'"

    def __set_noise(self, image, text='Black'):
        """
        半透明水印，布满整张图，并且自动旋转45°
        params:
            image:图片
            text:文字
        """

        # 添加背景
        new_img = Image.new('RGBA', (image.size[0] * 3, image.size[1] * 3), (255, 255, 255, 255))
        new_img.paste(image, image.size)
        # 添加水印
        rgba_image = new_img.convert('RGBA')
        text_overlay = Image.new('RGBA', rgba_image.size, (0, 0, 0, 0))
        image_draw = ImageDraw.Draw(text_overlay)

        for i in range(0, rgba_image.size[0], 50):
            for j in range(0, rgba_image.size[1], 50):
                image_draw.text((i, j), text, font=self.__font, fill=(0, 0, 0, 30))
        text_overlay = text_overlay.rotate(-45)
        image_with_text = Image.alpha_composite(rgba_image, text_overlay)
        image_with_text = image_with_text.crop((image.size[0], image.size[1], image.size[0] * 2, image.size[1] * 2))
        return image_with_text

    def __set_text_color(self, start=80, end=160):
        r = self.__random_color(start, end)
        g = self.__random_color(start, end)
        b = self.__random_color(start, end)
        return r, g, b

    def __create_cache_content(self):
        """获取随机字符"""
        normal_content = self.__random_text(self.__normal, self.__num - 1)
        special_content = self.__random_text(self.__special, 1)
        normal_content.extend(special_content)
        shuffle(normal_content)
        idx = normal_content.index(special_content[0])
        return normal_content, idx, special_content[0]

    def __get_true_position(self, idx):
        x, y = (idx % int(self.__width // 20)) * self.__cell_size, (idx // int(self.__width // 20)) * self.__cell_size
        return x, y

    @staticmethod
    def __random_color(start=100, end=240):
        return randint(start, end)

    @staticmethod
    def __random_text(source_content, need_count: int):
        return sample(source_content, need_count)


class CutImageCache:

    def __init__(self, w, h):
        self.__path = os.path.join(BASE_DIR, 'utils', 'cache_images')
        self.__path_ = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        self.__font = ImageFont.truetype(os.path.join(self.__path_, 'static', 'fonts', 'simhei.ttf'), 15)  # 加载字体 是Windows自带的
        self.__size = int(w // 40)  # 指定横竖图片个数
        self.__step = 40
        self.__image_step = 32
        self.__all_file_map = {
            'caomei.png': "请点击上部图片中的草莓",
            'daxiang.png': "请点击上部图片中的大象",
            'hudie.png': "请点击上部图片中的蝴蝶",
            'huli.png': "请点击上部图片中的狐狸",
            'huolongguo.png': "请点击上部图片中的火龙果",
            'li.png': "请点击上部图片中的梨",
            'lianwuguo.png': "请点击上部图片中的莲雾果",
            'liulian.png': "请点击上部图片中的榴莲",
            'luobo.png': "请点击上部图片中的萝卜",
            'mangguo.png': "请点击上部图片中的芒果",
            'mihoutao.png': "请点击上部图片中的猕猴桃",
            'moon.png': "请点击上部图片中的月亮",
            'naozhong.png': "请点击上部图片中的闹钟",
            'pangxie.png': "请点击上部图片中的螃蟹",
            'pingguo.png': "请点击上部图片中的苹果",
            'putao.png': "请点击上部图片中的葡萄",
            'qianbi.png': "请点击上部图片中的铅笔",
            'qingwa.png': "请点击上部图片中的青蛙",
            'sanjiaochi.png': "请点击上部图片中的三角尺",
            'shizi.png': "请点击上部图片中的红柿子",
            'shoubing.png': "请点击上部图片中的游戏手柄",
            'sun.png': "请点击上部图片中的太阳",
            'wugui.png': "请点击上部图片中的乌龟",
            'xigua.png': "请点击上部图片中的西瓜",
            'yazi.png': "请点击上部图片中的鸭子",
            'yu.png': "请点击上部图片中的鱼",
            'zhu.png': "请点击上部图片中的小猪"
        }
        self.__file_list = ['caomei.png', 'daxiang.png', 'hudie.png', 'huli.png', 'huolongguo.png', 'li.png', 'lianwuguo.png', 'liulian.png', 'luobo.png', 'mangguo.png', 'mihoutao.png', 'moon.png', 'naozhong.png', 'pangxie.png', 'pingguo.png', 'putao.png', 'qianbi.png', 'qingwa.png', 'sanjiaochi.png', 'shizi.png', 'shoubing.png', 'sun.png', 'wugui.png', 'xigua.png', 'yazi.png', 'yu.png', 'zhu.png']
        random.shuffle(self.__file_list)

    @staticmethod
    def __random_reshape_image(old_image):
        """对图片进行旋转"""
        random_list = range(-5, 6)  # 不规则旋转
        random_list = [0, 90, 180, 270]
        mini_im = old_image.rotate(random.choice(random_list), expand=True)
        mini_im = mini_im.resize((32, 32))
        return mini_im

    def __draw(self, items: list, tips, special_index=None):
        big_im = Image.new('RGBA', (self.__size * self.__step, self.__size * self.__step + 40), (180, 180, 180, 10))
        if special_index is None:
            for i in range(self.__size):
                for j in range(self.__size):
                    file_name = items.pop(0)
                    mini_im = Image.open(os.path.join(self.__path, file_name))
                    # mini_im = self.__random_reshape_image(mini_im)  # 对图像进行旋转
                    big_im.paste(mini_im,
                                 ((j * self.__step) + int((self.__step - self.__image_step) // 2),
                                  (i * self.__step) + int((self.__step - self.__image_step) // 2),
                                  ((j + 1) * self.__step) - int((self.__step - self.__image_step) // 2),
                                  ((i + 1) * self.__step) - int((self.__step - self.__image_step) // 2))
                                 )
        else:
            inx = 0
            rotate = random.choice([90, 180, 270])
            for i in range(self.__size):
                for j in range(self.__size):
                    file_name = items.pop(0)
                    mini_im = Image.open(os.path.join(self.__path, file_name))
                    if inx != special_index:
                        mini_im = mini_im.rotate(rotate, expand=True)
                    inx += 1
                    big_im.paste(mini_im,
                                 ((j * self.__step) + int((self.__step - self.__image_step) // 2),
                                  (i * self.__step) + int((self.__step - self.__image_step) // 2),
                                  ((j + 1) * self.__step) - int((self.__step - self.__image_step) // 2),
                                  ((i + 1) * self.__step) - int((self.__step - self.__image_step) // 2))
                                 )
        big_im.paste(tips, (0, self.__size * 40, self.__size * 40, (self.__size + 1) * 40))
        big_im = self.__set_noise(big_im)
        return big_im

    def __set_noise(self, image, text='Black'):
        """
        半透明水印，布满整张图，并且自动旋转45°
        params:
            image:图片
            text:文字
        """

        # 添加背景
        new_img = Image.new('RGBA', (image.size[0] * 3, image.size[1] * 3), (255, 255, 255, 255))
        new_img.paste(image, image.size)
        # 添加水印
        rgba_image = new_img.convert('RGBA')
        text_overlay = Image.new('RGBA', rgba_image.size, (0, 0, 0, 0))
        image_draw = ImageDraw.Draw(text_overlay)
        for i in range(0, rgba_image.size[0], 50):
            for j in range(0, rgba_image.size[1], 50):
                image_draw.text((i, j), text, font=self.__font, fill=(0, 0, 0, 50))
        text_overlay = text_overlay.rotate(-45)
        image_with_text = Image.alpha_composite(rgba_image, text_overlay)
        image_with_text = image_with_text.crop((image.size[0], image.size[1], image.size[0] * 2, image.size[1] * 2))
        return image_with_text

    def __get_position(self, index_):
        row = index_ // self.__size  # 第几行
        col = index_ % self.__size  # 第几列
        x = col * 40 + 20
        y = row * 40 + 20
        return x, y

    def __draw_tips(self, tips):
        """添加提示文字"""
        tips_draw_img = Image.new("RGBA", (self.__size * 40, 40), (180, 180, 180, 10))
        tips_draw = ImageDraw.Draw(tips_draw_img)  # 创建画布
        text_width = self.__font.getsize(tips)
        # 计算字体位置
        text_coordinate = int((self.__size * 40 - text_width[0]) / 2), int((40 - text_width[1]) / 2)
        tips_draw.text(text_coordinate, text=tips, font=self.__font, fill="rgb(20,20,20)", direction=None)
        return tips_draw_img

    def __draw_rotate(self, special):
        """返回旋转图"""
        draw = [special] * self.__size ** 2
        special_index = random.randint(0, len(draw)-1)
        position_x, position_y = self.__get_position(special_index)
        print(position_x, position_y, special_index)
        tips = self.__all_file_map[special]
        # 重组tips
        tips = f"请点击方向不同的{re.search('中的(.*)', tips).group(1)}"
        tips_draw_img = self.__draw_tips(tips)
        im = self.__draw(draw, tips_draw_img, special_index=special_index)
        return im, position_x, position_y, tips

    def __draw_normal(self, special, draw):
        special_index = draw.index(special)
        tips = self.__all_file_map[special]
        tips_draw_img = self.__draw_tips(tips)
        position_x, position_y = self.__get_position(special_index)
        im = self.__draw(draw, tips_draw_img)
        return im, position_x, position_y, tips

    def get_one(self):
        byte_image = BytesIO()
        special = self.__file_list.pop()
        # 正常比例与特殊比例3比1
        if int(time.time()) % 3:
            draw = [special]
            draw.extend(random.sample(self.__file_list * 10, self.__size ** 2 - 1))
            random.shuffle(draw)
            im, position_x, position_y, tips = self.__draw_normal(special, draw)
        else:
            im, position_x, position_y, tips = self.__draw_rotate(special)
        im.save(byte_image, 'png')
        # im.show()
        return byte_image.getvalue(), (position_x, position_y), tips


if __name__ == "__main__":
    w = random.choice([200, 240, 280, 400])
    h = random.choice([100, 200, 300, 400])
    # d = DrawCache(w, h)
    # a, b, c = d.get_one()
    # print(base64.b64encode(a).decode())
    # with open('a.png', 'wb') as f:
    #     f.write(a)
    # print(b, c)
    a = CutImageCache(w=w, h=w)
    print(a.get_one())
